from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File
from pydantic import BaseModel, Field
from typing import Any, Dict, List, Optional
import os
import uuid as _uuid
from sqlalchemy.ext.asyncio import AsyncSession

from db.session import get_db
from schemas.proofmapper import (
    SnapshotCreate,
    SnapshotUpdate,
    SnapshotResponse,
    SnapshotListResponse,
)
from crud.proofmapper import (
    create_snapshot,
    get_snapshot_by_id,
    get_snapshots_by_assessment_type,
    update_snapshot,
    delete_snapshot,
)
from log import log_error, log_info
from agent.ProofMapper.utils.json_modifier import json_modifier
from core.proofMapper_agent import (
    create_session_from_json_async,
    create_session_async,
    get_shared_async,
    apply_user_input_async_by_id,
    rollback_shared_async_by_id,
    delete_session_async,
)

router = APIRouter(
    prefix="/proofmapper",
    tags=["proofmapper"],
    responses={404: {"description": "Snapshot not found"}},
)

# 记录会话产物：shared_id -> {"pdf_path": str, "output_path": str}
session_artifacts: Dict[str, Dict[str, str]] = {}


@router.get("/snapshots/{assessment_type}", response_model=SnapshotListResponse)
async def get_snapshots(
    assessment_type: str,
    db: AsyncSession = Depends(get_db),
) -> SnapshotListResponse:
    """获取指定 assessment_type 下的所有快照列表"""
    snapshots = await get_snapshots_by_assessment_type(db, assessment_type)
    log_info(f"Fetched {len(snapshots)} snapshots for assessment_type={assessment_type}")
    return SnapshotListResponse(
        snapshots=[SnapshotResponse.model_validate(s) for s in snapshots],
        total=len(snapshots)
    )


@router.get("/snapshots/{assessment_type}/{snapshot_id}", response_model=SnapshotResponse)
async def get_snapshot_detail(
    assessment_type: str,
    snapshot_id: str,
    db: AsyncSession = Depends(get_db),
) -> SnapshotResponse:
    """获取单个快照详情并初始化 ProofMapper 会话"""
    snapshot = await get_snapshot_by_id(db, snapshot_id)
    if not snapshot or snapshot.assessment_type != assessment_type:
        raise HTTPException(status_code=404, detail="Snapshot not found")

    # 将数据库对象映射为 Pydantic 模型并转为 dict
    snapshot_model = SnapshotResponse.model_validate(snapshot)
    snapshot_dict = snapshot_model.model_dump()

    # 基于快照初始化 ProofMapper 会话，返回 shared_id
    shared_id, _ = await create_session_from_json_async(snapshot_dict)

    # 返回包含 shared_id 的响应
    snapshot_dict['shared_id'] = shared_id
    return SnapshotResponse(**snapshot_dict)


# =============================
class SharedResponse(BaseModel):
    final_output: Dict[str, Any] = Field(default_factory=dict)
    modification_log: List[Dict[str, Any]] = Field(default_factory=list)
    error_log: List[str] = Field(default_factory=list)


class UserInputRequest(BaseModel):
    user_input: str


# =============================
# 新增接口：文件解析/会话管理/交互修改/回滚
# =============================

@router.post("/parse", status_code=status.HTTP_200_OK)
async def parse_pdf(file: UploadFile = File(...)) -> Dict[str, Any]:
    """
    上传 PDF 文件，保存到 static/proofmapper 目录，解析并创建 ProofMapper 会话。
    返回 shared_id 与当前 shared 概要。
    """
    # 校验文件类型
    if file.content_type not in ("application/pdf", "application/octet-stream"):
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="仅支持上传 PDF 文件")

    # 计算保存目录（相对于当前文件）
    base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "static", "proofmapper"))
    os.makedirs(base_dir, exist_ok=True)

    # 生成唯一文件名，保留 .pdf 后缀
    filename = f"{_uuid.uuid4().hex}.pdf"
    save_path = os.path.join(base_dir, filename)

    # 保存上传内容到磁盘
    content = await file.read()
    try:
        with open(save_path, "wb") as f:
            f.write(content)
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"保存文件失败: {e}")

    # 输出 JSON 路径与 PDF 同名（唯一会话名），避免并发覆盖
    output_path = os.path.join(base_dir, f"{os.path.splitext(filename)[0]}.json")

    # 创建会话
    shared_id, shared = await create_session_async(save_path, output_path)
    # 记录产物路径，供删除会话时清理
    session_artifacts[shared_id] = {"pdf_path": save_path, "output_path": output_path}

    # 成功后删除已上传的临时PDF文件（保留解析生成的 JSON）
    try:
        os.remove(save_path)
    except Exception:
        # 忽略清理失败，不影响主要流程
        log_error(f"解析文件成功，删除文件失败请检查路径: {save_path}")

    return {"shared_id": shared_id, **shared}


@router.get("/sessions/{shared_id}", response_model=SharedResponse)
async def get_session_shared(shared_id: str) -> SharedResponse:
    """获取指定会话的当前 shared（精简版）。"""
    shared = await get_shared_async(shared_id)
    if shared is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Session not found")
    return SharedResponse(**shared)


@router.get("/sessions/{shared_id}/history")
async def get_session_history(shared_id: str) -> Dict[str, Any]:
    """查看指定会话的修改历史与摘要。"""
    shared = await get_shared_async(shared_id)
    if shared is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Session not found")
    modification_log = shared.get("modification_log", [])
    summary = json_modifier.get_history_summary(modification_log)
    return {"modification_log": modification_log, "summary": summary}


@router.post("/sessions/{shared_id}/apply", response_model=SharedResponse)
async def apply_user_input(shared_id: str, body: UserInputRequest) -> SharedResponse:
    """对会话应用一次自然语言修改，返回更新后的 shared。"""
    updated = await apply_user_input_async_by_id(shared_id, body.user_input)
    if updated is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Session not found")
    return SharedResponse(**updated)


@router.post("/sessions/{shared_id}/rollback", response_model=SharedResponse)
async def rollback_session(shared_id: str) -> SharedResponse:
    """回滚会话最近一次修改。"""
    updated = await rollback_shared_async_by_id(shared_id)
    if updated is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Session not found")
    return SharedResponse(**updated)


@router.delete("/sessions/{shared_id}")
async def delete_session(shared_id: str) -> Dict[str, Any]:
    """删除会话。"""
    # 优先清理产物（JSON 与 可能残留的 PDF）
    artifacts = session_artifacts.pop(shared_id, None)
    if artifacts:
        for label, path in artifacts.items():
            try:
                if path and os.path.exists(path):
                    os.remove(path)
            except Exception as e:
                log_error(f"删除会话产物失败: {label}={path}, 错误: {e}")

    ok = await delete_session_async(shared_id)
    if not ok:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Session not found")
    return {"ok": True}


@router.post("/snapshots", response_model=SnapshotResponse, status_code=status.HTTP_201_CREATED)
async def create_snapshot_api(
    payload: SnapshotCreate,
    db: AsyncSession = Depends(get_db),
) -> SnapshotResponse:
    """创建新快照"""
    snapshot = await create_snapshot(
        db,
        assessment_type=payload.assessment_type,
        name=payload.name,
        evaluation_items=[item.model_dump() for item in payload.evaluation_items],
    )
    log_info(f"Snapshot created: id={snapshot.id}, assessment_type={payload.assessment_type}, name={payload.name}")
    return SnapshotResponse.model_validate(snapshot)


@router.put("/snapshots/{snapshot_id}", response_model=SnapshotResponse)
async def update_snapshot_api(
    snapshot_id: str,
    payload: SnapshotUpdate,
    db: AsyncSession = Depends(get_db),
) -> SnapshotResponse:
    """更新指定快照"""
    snapshot = await update_snapshot(
        db,
        snapshot_id,
        name=payload.name,
        evaluation_items=[item.model_dump() for item in payload.evaluation_items] if payload.evaluation_items else None,
    )
    if not snapshot:
        raise HTTPException(status_code=404, detail="Snapshot not found")

    log_info(f"Snapshot updated: id={snapshot_id}")
    return SnapshotResponse.model_validate(snapshot)


@router.delete("/snapshots/{snapshot_id}")
async def delete_snapshot_api(
    snapshot_id: str,
    db: AsyncSession = Depends(get_db),
) -> Dict[str, Any]:
    """删除指定快照"""
    success = await delete_snapshot(db, snapshot_id)
    if not success:
        raise HTTPException(status_code=404, detail="Snapshot not found")

    log_info(f"Snapshot deleted: id={snapshot_id}")
    return {"ok": True, "id": snapshot_id}
